import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.util.Stack;
import processing.core.*;
public class Sketch extends PApplet {

	/**
	 * Tonderai Nemarundwe Robotics 478 Genetic + Search Algorithms
	 */
	private static final long serialVersionUID = 1L;
	int popSize = 150;
	int genNumber =0;
	public int bestFitness; //best score
	public int worstFitness; //worst score
	public int fittestChromosomeIndex; //index of the fittest chromosome in the current population
	public int totalFitness; //sum of all fitness
	double crossoverRate=0.1,mutationRate=0.7;
	boolean flag = true;
	int scale = 14; // must be >=2 to render correctly
	int mazeWidth = 12; // width in # of cells
	int mazeHeight = 12; // height in # of cells
	int chromosomeLength = 2*mazeWidth * mazeHeight;
	int spacing = 10; //size of border around maze in pixels
	int fps = 1000; //fps to run animation at
	public int [] path;
	public boolean geneticDone = true;
	public Stack<Cell> stack = new Stack<Cell>(); //stack for DFS
	public Stack<Cell> Solvestack = new Stack<Cell>(); //stack for DFS
	public Maze maze = new Maze(mazeWidth, mazeHeight); //the maze
	Cell c; //to hold the active Cell
	Cell nextC; //to hold the upcoming cell
	//genetic class initialization
	public Genetic g = new Genetic(crossoverRate, mutationRate, popSize,chromosomeLength, mazeWidth, mazeHeight); 
	FileWriter fstream; //= new FileWriter("out.txt",true);
	BufferedWriter out; //= new BufferedWriter(fstream);
	@Override
	public void setup() {
		
		try{
			  // Create file 
			  fstream = new FileWriter("c:\\temp\\curve.csv",true);
			  out = new BufferedWriter(fstream);
			  //out.write("Hello Java");
			  //Close the output stream
			 // out.close();
		}catch (Exception e){//Catch exception if any
			  System.err.println("Error: " + e.getMessage());
		}
				
		size(200,200); //set size of the sketch
		frameRate(fps); //set fps
		ellipseMode(CORNER); //ellipses drawn using top-left corner as origin
		smooth(); //draw with anti-aliasing
		maze.getCell(mazeWidth - 1, mazeHeight - 1).setEnd(); // set bottom right Cell as END point
		maze.getCell(0,0).setStart(); //starting cell is the top-left cell
		c = maze.getCell(Rand.randomInt(mazeWidth),Rand.randomInt(mazeHeight)); //starting cell for algorithm is random!
		c.setVisited(); //mark it visited
		
		//go through the algorithm's process once so the stack isn't empty
		nextC = c.randomNeighbor(); //pick a random neighbor, nextC
		nextC.setVisited(); //mark it visited
		c.breakWall(nextC); //break the walls between c and nextC
		stack.push(c); //push c onto the stack
		c = nextC; //recurse with nextC as the new c
	}

	@Override
	public void draw() { //automatically loops at specified frameRate
		background(49,81,88); //black out background (clearing any drawing)
		maze.drawMaze(this); //draw the maze
		maze.markMaze(this); //mark start
		maze.markEnd(this); //mark end
		if (!stack.isEmpty()) {//if the maze is incomplete || account for stack starting empty
			if (c.isDeadEnd() || c.isEnd() || c.isStart()) { //if c has no unvisited neighbors, or it's the start/end cell, start backtracking...
				nextC = stack.pop();//by popping the last cell off the stack...
				c = nextC;//and recursing with it as c
			} else { // if there are unvisited neighbors...
				nextC = c.randomNeighbor(); //pick a random neighbor, nextC
				nextC.setVisited(); //mark it visited
				c.breakWall(nextC); //break the walls between c and nextC
				stack.push(c); //push c onto the stack
				c = nextC; //recurse with nextC as the new c
			}
			//c.ispath = true;
			stroke(204,0,0); //stroke black
			fill(204,0,0); //fill white
			ellipse(c.getX() * scale + spacing + 1, c.getY() * scale + spacing + 1, scale - 2, scale - 2); // draw 'stylus' ellipse
		}
		else if(!geneticDone){
			g.Epoch();
			if(!g.SolutionFound()){				
				if(g.GetPopConverged()){
					println("Population converged! No solution");
					noLoop();
				}
				if(g.generation%50 == 0){
					for(int i=0;i<mazeWidth;i++)
						for(int j=0;j<mazeHeight;j++){
							if(g.GetBestPath()[i][j] >= 1){
								Cell c = maze.getCell(i, j);
								c.drawCellPath(this);
								//saveFrame();
							}
						}
				}
			}
			else{
				geneticDone = true;
				try {
					out.write(g.generation+"\n");
					out.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				  //Close the output stream
				 // out.close();
				println("Genetic done!");
				for(int i=0;i<mazeWidth;i++)
					for(int j=0;j<mazeHeight;j++){
						if(g.GetBestPath()[i][j] >= 1){
							Cell c = maze.getCell(i, j);
							c.drawCellPath(this);
							noLoop(); //stop looping the draw() method
						}
					}
			}
		}

		else{ //maze is finished
			if(flag){
				println("Click on maze to solve!");
				flag = false;
			}

		}


	}
	@Override
	public void mousePressed() {

		geneticDone=false;
		Scanner in = new Scanner(System.in);
		String mutation, xover, scale,stype;
		println("Choose genetic variables: ");
		println("MutationType {RM(random), EM(exchange)}  ");
		mutation = in.nextLine();
		println("CrossoverType{OPX, TPX, UX}");
		xover = in.nextLine();
		println("ScaleType{NONE, RANK, SIGMA, BOLTZMANN} ");
		scale = in.nextLine();
		println("SelectionType{RWS(roulette), TS(tournament), ATS(alternate)}");
		stype = in.nextLine();
		in.close();
		if(mutation.contains("RM"))
			g.mutType = MutationType.RM;
		if(mutation.contains("EM"))
			g.mutType = MutationType.EM;
		if(xover.contains("OPX"))
			g.xtype = CrossoverType.OPX;
		if(xover.contains("TPX"))
			g.xtype = CrossoverType.TPX;
		if(xover.contains("UX"))
			g.xtype = CrossoverType.UX;
		if(stype.contains("RWS"))
			g.selType = SelectionType.RWS;
		if(stype.contains("TS"))
			g.selType = SelectionType.TS;
		if(stype.contains("ATS"))
			g.selType = SelectionType.ATS;
		if(scale.contains("RANK"))
			g.scType = ScaleType.RANK;
		if(scale.contains("SIGMA"))
			g.scType = ScaleType.SIGMA;
		if(scale.contains("BOLT"))
			g.scType = ScaleType.BOLTZMANN;
		
		g.Run(); //start genetic solver

	}

	static public void main(String args[]) {
		PApplet.main(new String[] { "Sketch" }  )  ;
	}
}
